#include "Camera.h"
#include"DebugManager.h"

Camera::Camera()
{
}

void Camera::setPos(glm::vec3 position, glm::vec3 target, glm::vec3 upDirection)
{
    this->Position = position;
    this->Target = target;
    this->UpDirection = upDirection;

}

void Camera::ProcessKeyboard(float deltaTime) {
    // 计算相机与目标之间的距离
    float distance = glm::length(Target - Position);

    // 根据距离调整速度，这里的2.5f是基础速度，可以根据需要调整
    // 例如，使用距离的一部分来增加速度，确保距离越远，速度越快
    float velocity = 2.5f * deltaTime * (1.0f + distance * 0.5f); // 0.1f是调整因子，可以根据实际效果调整

    glm::vec3 forward = glm::normalize(Target - Position);
    glm::vec3 right = glm::normalize(glm::cross(forward, UpDirection));
    glm::vec3 up = glm::normalize(glm::cross(right, forward));

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        Position += forward * velocity;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        Position -= forward * velocity;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        Position -= right * velocity;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        Position += right * velocity;

    // 处理上下左右的移动
    if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
        Position += velocity * up;
        Target += velocity * up;
    }
    if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
        Position -= velocity * up;
        Target -= velocity * up;
    }
    if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
        Position -= right * velocity;
        Target -= right * velocity;
    }
    if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
        Position += right * velocity;
        Target += right * velocity;
    }
}

void Camera::ProcessMouseMovement(double xpos, double ypos) {
    if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
        if (!selectedModel) {
            // 如果当前没有选中的模型，则尝试选中模型
            selectedModel = FindSelectedModel(xpos, ypos);
            if (selectedModel) {
                // 如果是第一次选中模型，记录当前鼠标位置
                lastX = xpos;
                lastY = ypos;
                return;
            }
        }


        if (selectedModel) {
            // 计算鼠标移动的偏移量
            float xoffset = xpos - lastX;
            float yoffset = lastY - ypos;
            lastX = xpos;
            lastY = ypos;

            // 设置旋转灵敏度
            float sensitivity = 1.0f;
            xoffset *= sensitivity;
            yoffset *= sensitivity;

            glm::vec3 forward = glm::normalize(Target - Position);
            glm::vec3 right = glm::normalize(glm::cross(forward, UpDirection));
            glm::vec3 up = glm::normalize(glm::cross(right, forward));

            glm::vec3 yawAxis = up; // 使用相机的上方向作为yaw轴
            glm::vec3 pitchAxis = -right; // 使用相机的左方向（右方向的反方向）作为pitch轴

            // 计算合成旋转轴
            glm::vec3 combinedAxis = glm::normalize(yawAxis * xoffset + pitchAxis * yoffset);

            // 计算旋转角度
            float angle = glm::length(glm::vec2(xoffset, yoffset)); // 使用二维向量长度作为旋转角度

            // 创建四元数旋转
            glm::quat rotation = glm::angleAxis(glm::radians(angle), combinedAxis);

            // 更新模型的旋转状态
            glm::quat initialQuat = selectedModel->rotation;
            glm::quat newQuat = rotation * initialQuat;
            selectedModel->rotation = glm::normalize(newQuat); // 正规化四元数以避免累积误差
        }
    }
    else {
        // 如果鼠标左键没有按下，则清空选中的模型
        selectedModel = nullptr;
    }
}

glm::vec3 Camera::ScreenToWorldCoords(double xpos, double ypos) {
    // 获取视口大小
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);

    // 将鼠标坐标从屏幕空间转换到标准化设备坐标（NDC）空间
    float x = xpos;
    float y = height - ypos; // 转换y坐标以适应OpenGL的坐标系统

    // 将NDC坐标转换到世界空间
    glm::vec4 viewport(0.0f, 0.0f, width, height);

    glm::mat4 projection = GetProjectionMatrix();
    glm::mat4 view = GetViewMatrix();

    // 转换到世界坐标的远平面点
    glm::vec3 farPoint = glm::unProject(glm::vec3(x, y, 1.0f), view, projection, viewport);

    // 可以返回近平面点，或者使用nearPoint和farPoint来定义射线
    return farPoint;
}

Model* Camera::FindSelectedModel(double xpos, double ypos) {
    DrawRay(xpos, ypos);
    glm::vec3 rayOrigin = Position;
    glm::vec3 rayDirection = CalculateRayDirection(xpos, ypos);
    Model* selectedModel = nullptr;
    float minDistance = std::numeric_limits<float>::max();

    for (auto& model : modelsInView) {
        glm::vec3 minBounds, maxBounds;
        // 获取变换后的包围盒
        std::tie(minBounds, maxBounds) = GetTransformedBounds(model);

        float tMin, tMax;
        // 射线检测
        std::tie(tMin, tMax) = IntersectRayAABB(rayOrigin, rayDirection, minBounds, maxBounds);

        if (tMax > 0 && tMin < tMax && tMin < minDistance) {
            minDistance = tMin;
            selectedModel = model;
        }
    }

    return selectedModel;
}

glm::vec3 Camera::CalculateRayDirection(double xpos, double ypos) {
    return glm::normalize(ScreenToWorldCoords(xpos, ypos) - Position);
}

std::pair<glm::vec3, glm::vec3> Camera::GetTransformedBounds(Model* model) {
    glm::mat4 modelMatrix = model->GetTransformMatrix();
    glm::vec3 initialMinBounds = model->GetInitialMinBounds();
    glm::vec3 initialMaxBounds = model->GetInitialMaxBounds();
    glm::vec3 transformedMinBounds = glm::vec3(modelMatrix * glm::vec4(initialMinBounds, 1.0f));
    glm::vec3 transformedMaxBounds = glm::vec3(modelMatrix * glm::vec4(initialMaxBounds, 1.0f));
    return { glm::min(transformedMinBounds, transformedMaxBounds), glm::max(transformedMinBounds, transformedMaxBounds) };
}

std::pair<float, float> Camera::IntersectRayAABB(const glm::vec3& rayOrigin, const glm::vec3& rayDirection, const glm::vec3& minBounds, const glm::vec3& maxBounds) {
    float tMin = 0.0f;
    float tMax = std::numeric_limits<float>::max();

    for (int i = 0; i < 3; ++i) {
        if (std::abs(rayDirection[i]) < 1e-6) {
            if (rayOrigin[i] < minBounds[i] || rayOrigin[i] > maxBounds[i]) {
                return { -1.0f, -1.0f };
            }
        }
        else {
            float ood = 1.0f / rayDirection[i];
            float t1 = (minBounds[i] - rayOrigin[i]) * ood;
            float t2 = (maxBounds[i] - rayOrigin[i]) * ood;
            if (t1 > t2) std::swap(t1, t2);
            tMin = std::max(tMin, t1);
            tMax = std::min(tMax, t2);
            if (tMin > tMax) return { -1.0f, -1.0f };
        }
    }
    return { tMin, tMax };
}

void Camera::DrawRay(double xpos, double ypos) {
    glm::vec3 rayDirection = CalculateRayDirection(xpos, ypos); 
    glm::vec3 rayOrigin = Position - glm::normalize(UpDirection)* 10.0f;

    Ray ray(rayOrigin, rayDirection);
    DebugManager::getInstance().addRay(ray);
}